﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Nina
{
    /// <summary>
    /// A socket acceptor for server instances
    /// </summary>
    public class NinaSocketAcceptor : NinaService
    {
        protected TcpListener m_listener;
        protected bool m_accepting;

        /// <summary>
        /// Constructor
        /// </summary>
        public NinaSocketAcceptor()
            : base()
        {
            m_listener = null;
            m_accepting = false;
        }

        /// <summary>
        /// Binds the socket to the local machine and starts accepting clients
        /// </summary>
        /// <param name="port">Port number</param>
        public void Bind(int port)
        {
            string hostIp = NinaSession.LocalIPAddress;

            /* Initialise binding */
            IPAddress ipAddress = IPAddress.Parse(hostIp);
            this.Bind(ipAddress, port);
        }

        /// <summary>
        /// Binds the socket and starts accepting clients
        /// </summary>
        /// <param name="host">Host IP address</param>
        /// <param name="port">Port number</param>
        public void Bind(IPAddress host, int port)
        {
            if (m_listener != null)
                throw new Exception("");

            m_listener = new TcpListener(host, port);

            /* Build a thread for accepting clients */
            Thread t = new Thread(new ParameterizedThreadStart(delegate
            {
                Socket socket;
                NinaSession session;

                m_listener.Start();
                m_accepting = true;
                while (m_accepting)
                {
                    /* Accept any client and queue them for processing */
                    lock (m_listener)
                    {
                        while (m_listener.Pending())
                        {
                            socket = m_listener.AcceptSocket();
                            session = new NinaSession(socket);
                            ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessSession), session);
                        }
                    }

                    /* Sleep briefly */
                    try
                    {
                        Thread.Sleep(100);
                    }
                    catch { }
                }

            }));
            /* Start the thread */
            t.Start();
        }

        /// <summary>
        /// Attempts to unbind the socket
        /// </summary>
        /// <returns>True on success</returns>
        public bool Unbind()
        {
            try
            {
                lock (m_listener)
                {
                    m_listener.Stop();
                }
                m_accepting = false;
                return true;
            }
            catch { }
            return false;
        }
    }
}
